/*
 * @Author: 林子卓 2297304135@qq.com
 * @Date: 2023-11-16 15:45:32
 * @LastEditors: 林子卓 2297304135@qq.com
 * @LastEditTime: 2023-11-17 09:12:53
 * @FilePath: /process_pool/ProcessPool.cpp
 * @Description: 利用管道创建一个简易进程池
 */

// 这里规定
// 输入型参数：const &
// 输出型参数：*
// 输入+输出：&
// 父进程传输给子进程的一定是一个4字节的int变量
// 根据int变量进行不同的任务执行

// 子进程读取，父进程写入

#include "Task.hpp"
#include <string>
#include <ctime>
#include <unistd.h>
#include <cstdlib>
#include <cassert>
#include <sys/wait.h>
#include <sys/stat.h>

// 先描述
class channel
{
public:
	channel(const int InteractFd, const std::string &ProcessName, const pid_t WorkerId)
		: _InteractFd(InteractFd), _ProcessName(ProcessName), _WorkerId(WorkerId)
	{
	}

public:
	int _InteractFd;
	std::string _ProcessName;
	pid_t _WorkerId;
};

// 将对进程的管理转变为对管道文件的管理再转变为对vector的管理
const int ProcessNum = 5;
const int TaskNum = 100;

Task_list tasks;

void worker()
{
	// 子进程不断读取父进程中的信息
	while (true)
	{
		int ReceiveTask = 0;
		ssize_t n = read(0, &ReceiveTask, sizeof(ReceiveTask));
		std::cout << "child get the task: " << ReceiveTask << ", my pid is: "
				  << getpid() << std::endl;
		if(n == sizeof(int))
		{
			tasks[ReceiveTask]();
		}
		else if(n == 0) break;
	}
}

void InitChannel(std::vector<channel> *channels)
{
	for (int i = 0; i < ProcessNum; ++i)
	{
		int pipefd[2];
		int n = pipe(pipefd);
		assert(n == 0);
		// realse条件下assert不起作用
		// 为了防止报警告，对n进行使用
		(void)n;
		pid_t id = fork();
		assert(id >= 0);

		// 这里由于每次父进程都只会保留写端，读端关闭，所以每次生成的pipe读端都是3
		// 利用这一特性，可以利用dup2将端口全部重定向成标准输入端口
		if (id == 0)
		{
			// child
			close(pipefd[1]);
			dup2(pipefd[0], 0);
			close(pipefd[0]);
			worker();
			std::cout << "child process:" << getpid() << "quit!\n";
			// std::cout << "child read fd is: " << pipefd[0] << std::endl;
			// sleep(50);
			exit(0);
		}
		//注意位置！！！这段代码是主进程干的事，一定要放在子进程代码后面
		close(pipefd[0]);
		std::string name = "process-";
		name = name + std::to_string(i);

		channels->push_back(channel(pipefd[1], name, id));
		std::cout << "the " << i << " channel's wfd is: " << pipefd[1] << std::endl;
	}
}

void debug1(const std::vector<channel> &channels)
{
	for (auto &iter : channels)
	{
		std::cout << "this channel is: " << iter._ProcessName << ", pid is: " << iter._WorkerId << ", wfd is: " << iter._InteractFd << std::endl;
	}
}

void CtrlChannel(const std::vector<channel> &channels)
{
	int which = 0;
	int sz = channels.size();
	while(true)
	{
		int select = 0;
		tasks.EnumPrint();
		std::cout << "enter the task number: ";
		std::cin >> select;

		if(select <= 0 || select > tasks.size()) break;

		// 在选择逻辑中均采用随机选择的方式
		// 1. 选择任务
		// int RunTask = rand() % tasks.size();
		int RunTask = select - 1;

		// 2. 选择进程
		// int RunChannel = rand() % channels.size();

		// 3. 进行指派
		std::cout << "father @ " << channels[which]._ProcessName << " to run task: " << RunTask << ", pid is " << channels[which]._WorkerId << std::endl;
		write(channels[which]._InteractFd, (void *)&RunTask, sizeof(RunTask));
		++which;
		if(which == sz) which = 0;
	}
}

void ClearChannel(const std::vector<channel> &channels)
{
	//利用一个特性，写段关闭，读端将会读到文件结尾，也就是读到0
	for(const auto& iter: channels) close(iter._InteractFd);
	for(const auto& iter: channels)
	{
		pid_t id = waitpid(iter._WorkerId, nullptr, 0);
		assert(id != -1);
		(void)id;
	}

}

int main()
{
	// 这里进行^操作只是为了让种子更分散一点
	srand(time(nullptr) ^ getpid() ^ 1023);
	// 再组织
	std::vector<channel> channels;

	// 初始化通道数组
	InitChannel(&channels);

	// 父进程向子进程传输指令
	CtrlChannel(channels);

	// debug: 判断子进程是否成功创建，并且对应的管道是否正确
	// debug1(channels);

  	//清理子进程资源
	ClearChannel(channels);
	return 0;
}